#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ================= Pseudo Disk =================== */

#include "../kernel/process.h"
#include "../kernel/cache.h"
#include "../kernel/fs.h"
#include "../kernel/disk.h"
struct process *current;
int num_sect_disk;

static struct process no_name;
static FILE *fp;

static int init_pdisk(const char *filename)
{
	char y[10];

	/* Open disk image file. */
	fp = fopen(filename, "r+b");
	if(fp == NULL) {
		printf("error open file.\n");
		return -1;
	}
	fseek(fp, 0, SEEK_END);
	num_sect_disk = ftell(fp) / SIZE_SECTOR;

	/**/
	current = &no_name;
	current->work_dir = 1;

	/**/
	init_cache();
	if(init_fs() != 0) {
		printf("The disk isn't formated. Do you want to format it to continue? [y/n]");
		scanf("%s", y);
		if(y[0] != 'n')
			test_format();
		else
			return -2;
	}
	return 0;
}

int rdwr_disk(u32_t begin, int count, u8_t *buf, int cmd, struct process **wait)
{
	int ret;

	ret = fseek(fp, begin * SIZE_SECTOR, SEEK_SET);
	if(ret != 0) {
		printf("fseek error! %d  %d\n", ret, begin);
		return -1;
	}

	if(cmd == WIN_READ)
		ret = fread(buf, SIZE_BLOCK, 1, fp);
	else
		ret = fwrite(buf, SIZE_BLOCK, 1, fp);
	if(ret != 1) {
		printf("read/write error!\n");
		return -1;
	}

	return 0;
}

int gettime(void)
{
	return 1234;
}
unsigned new_page(int f)
{
	return (unsigned)malloc(4096);
}

/* ================= FS Routines  =================== */

static int test_touch(int argc, char *argv[])
{
	int fd;
	if(argc != 2) {
		printf("please input file name.\n");
		return -1;
	}

	fd = open(argv[1], 2);
	if(fd < 0) {
		printf("error in create file.\n");
		return -1;
	}
	close(fd);
	return 0;
}

static int test_cat(int argc, char *argv[])
{
	int fd, ret = 0;
	char buf[513];

	if(argc != 2) {
		printf("please input file name.\n");
		return -1;
	}

	fd = open(argv[1], 0);
	if(fd < 0) {
		printf("error in open file.\n");
		return -1;
	}
	while(ret == 0) {
		ret = read(fd, buf, SIZE_BLOCK);
		buf[SIZE_BLOCK - ret] = '\0';
		printf("%s", buf);
	}

	close(fd);
	printf("\n");
	return 0;
}
static int test_copy(int argc, char *argv[])
{
	int fd, ret;
	FILE *fsource;
	char buf[SIZE_BLOCK];

	if(argc != 3) {
		printf("Invalid argument.\n");
		printf("Usage: %s sour dest.\n", argv[0]);
		printf("  @sour is file at local machine, and @dest is file at FS.\n");
		return -1;
	}

	/* Open source file. */
	fsource = fopen(argv[1], "r");
	if(fsource == NULL) {
		printf("error in open file %s.\n", argv[1]);
		return -1;
	}

	/* Open destination file. */
	fd = open(argv[2], 2);
	if(fd < 0) {
		printf("error in open file %s.\n", argv[2]);
		return -1;
	}

	while(1) {
		ret = fread(buf, 1, SIZE_BLOCK, fsource);
		write(fd, buf, ret);
		if(ret != SIZE_BLOCK)
			break;
	}
	close(fd);
	fclose(fsource);
	return 0;
}

static int test_ls(int argc, char *argv[])
{
	int n, r, file, len;
	char fname[100], path[100];
	struct inode st;

	strcpy(path, (argc==1 ? "/" : argv[1]));
	n = opendir(path);
	if(n < 0) {
		printf("error in open dir, errno: %d\n", n);
		return -1;
	}

	len = strlen(path);
	if(path[len - 1] != '/')
		path[len++] = '/';

	while(1) {
		r = readdir(n, fname);
		if (r == -1)
			printf("Wrong!\n");
		if (r == 0)
			break;

		strcpy(path + len, fname);
		file = open(path, 0);
		stat(file, &st);
		printf("%4d  %2d  %s\n", st.size, st.type, fname);
		close(file);
	}
	return 0;
}

int test_one(int argc, char *argv[])
{
	int n;
	if (argc == 1) {
		printf("please give disk image filename.\n");
		return -1;
	}
	if(strcmp(argv[0], "rm") == 0)
		n = remove(argv[1]);
	else if(strcmp(argv[0], "rmdir") == 0)
		n = rmdir(argv[1]);
	else if(strcmp(argv[0], "mkdir") == 0)
		n = mkdir(argv[1]);

	if (n < 0) {
		printf("error number: %d\n", n);
		return -1;
	}
	return 0;
}

int test_flush()
{
	flush();
}

int test_format()
{
	format();
	printf("The disk has been formated.\n");
}

int test_exit()
{
	exit(0);
}

static int help(int argc, char *argv[]);
static struct {char name[10]; int (*func)(); char desc[50];} commands[] = {
	{"exit", test_exit, "exit this program"},
	{"help", help, "display this list"},
	{"touch", test_touch,"create a file"},
	{"rm", test_one, "remove a file"},
	{"cat", test_cat, "display a file"},
	{"cp", test_copy, "copy a file from host to disk image !"},
	{"mkdir", test_one, "create a directory"},
	{"rmdir", test_one, "remove a directory"},
	{"ls", test_ls, "list files in a directory"},
	{"format", test_format, "make FS"},
	{"flush", test_flush, "flush all changes into disk image"},
};
static int help(int argc, char *argv[])
{
	int i;
	for(i = 0; i < sizeof(commands) / sizeof(commands[0]); i++)
		printf("%s\t%s\n", commands[i].name, commands[i].desc);
	return 0;
}

/* ================= Main Shell =================== */
static int split_args(char *arg_line, char **argv)
{
	int i = 0, flag = 1;
	int argc = 0;
	while (1) {
		if (arg_line[i] == '\0') {
			break;
		} else if (arg_line[i] == ' ') {
			arg_line[i] = '\0';
			flag = 1;
		} else if (flag == 1) {
			argv[argc++] = &arg_line[i];
			flag = 0;
		}
		i++;
	}
	return argc;
}

static void run_cmd(char *cmd, int argc, char **argv)
{
	int i;

	for(i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
		if(strcmp(cmd, commands[i].name) == 0)
			break;
	}
	if(i == sizeof(commands) / sizeof(commands[0])) {
		printf("Command not found!\n");
		return;
	}

	commands[i].func(argc, argv);
}

int main(int argc, char *argv[])
{
	char cmd[100];
	char *s_argv[10];
	int s_argc;

	if(argc == 1) {
		printf("Usage: %s image [command [arguments]]\n", argv[0]);
		return -1;
	}

	if(init_pdisk(argv[1]) != 0)
		return -1;

	/* Once-mode */
	if(argc > 2) {
		run_cmd(argv[2], argc - 2, argv + 2);
		test_flush();
		test_exit();
	}

	/* Shell-mode */
	while(1) {
		printf(">> ");
		gets(cmd);

		/* Only Enter. */
		if (cmd[0] == '\0')
			continue;

		/* Split the arguments. */
		s_argc = split_args(cmd, s_argv);

		run_cmd(cmd, s_argc, s_argv);
	}
}
